
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Diagnostics;
using System.Threading;

namespace Swaf.Container
{
	/// <summary>
	/// Summary description for ThreadSafeHashtable.
	/// </summary>
	[Serializable]
	public class ThreadSafeHashtable : IDictionary, ICloneable
	{
		protected Hashtable m_innerTable;
		protected bool m_readOnly = false;

		[NonSerialized] protected ReaderWriterLock m_accessSem = null;
		[NonSerialized] protected ICollection m_innerTableValues = null;
		[NonSerialized] protected ICollection m_innerTableKeys = null;

		public ThreadSafeHashtable()
		{
			m_innerTable = new Hashtable();
		}

		public ThreadSafeHashtable(ThreadSafeHashtable clone)
		{
			try
			{
				clone.AccessSem.AcquireReaderLock(-1);
				m_innerTable = (Hashtable)clone.m_innerTable.Clone();
			}
			finally
			{
				clone.AccessSem.ReleaseReaderLock();
			}
		}

		//TODO: Implement complementary Hashtable constructors for this class


		/// <summary>
		/// This method should be efficently thread safe.  
		/// </summary>
		protected ReaderWriterLock AccessSem
		{
			get
			{
				if(m_accessSem == null)
				{
					//By waiting to do a hard core lock on this until within the main if
					//statement, we only pay the hit of that the first time this method is
					//called for an instance.  However, multiple threads could think that they
					//need to create the access sem when many threads are calling into this
					//method on the first call.  The inner if statement after the lock is 
					//received fixes that to ensure the lock is only created once and that there
					//is only one lock instance per object.
					lock(this)
					{
						if(m_accessSem == null)
							m_accessSem = new ReaderWriterLock();
					}
				}
				//Every call into this method except the first will simply return the m_accessSem
				//without ever needing to lock anything.
				return m_accessSem;
			}
		}

		#region IDictionary Members

		public bool IsReadOnly 
		{
			get {return m_readOnly;}
		}

		public IDictionaryEnumerator GetEnumerator()
		{
			return new ThreadedHashtableEnumerator(this);
		}

		public object this[object key]
		{
			get
			{
				object v = null;
				try
				{
					AccessSem.AcquireReaderLock(-1);
					v = m_innerTable[key];
					return v;
				}
				finally
				{
					AccessSem.ReleaseReaderLock();
				}
			}
			set
			{
				try
				{
					AccessSem.AcquireWriterLock(-1);
					m_innerTable[key] = value;
				}
				finally
				{
					AccessSem.ReleaseWriterLock();
				}
			}
		}

		public void Remove(object key)
		{
			try
			{
				AccessSem.AcquireWriterLock(-1);
				m_innerTable.Remove(key);
			}
			finally
			{
				AccessSem.ReleaseWriterLock();
			}
		}

		public bool Contains(object key)
		{
			try
			{
				AccessSem.AcquireReaderLock(-1);
				bool b = m_innerTable.Contains(key);
				return b;
			}
			catch(Exception)
			{
				return false;
			}
			finally
			{
				AccessSem.ReleaseReaderLock();
			}
		}

		public void Clear()
		{
			try
			{
				AccessSem.AcquireWriterLock(-1);
				m_innerTable.Clear();
			}
			finally
			{
				AccessSem.ReleaseWriterLock();
			}
		}

		public ICollection Keys
		{
			get
			{
				if(m_innerTableKeys == null)
				{
					lock(this)
					{
						if(m_innerTableKeys == null)
							m_innerTableKeys = new ThreadedHashtableCollection(this, m_innerTable.Keys);
					}
				}
				return m_innerTableKeys;
			}
		}

		public ICollection Values
		{
			get
			{
				if(m_innerTableValues == null)
				{
					lock(this)
					{
						if(m_innerTableValues == null)
							m_innerTableValues = new ThreadedHashtableCollection(this, m_innerTable.Values);
					}
				}
				return m_innerTableValues;
			}
		}

		public void Add(object key, object value)
		{
			try
			{
				AccessSem.AcquireWriterLock(-1);
				m_innerTable.Add(key, value);
			}
			finally
			{
				AccessSem.ReleaseWriterLock();
			}
		}

		public bool IsFixedSize {get{return false;}}
		#endregion

		#region ICollection Members

		public bool IsSynchronized {get{return true;}}

		public int Count
		{
			get
			{
				try
				{
					AccessSem.AcquireReaderLock(-1);
					int c = m_innerTable.Count;
					return c;
				}
				finally
				{
					AccessSem.ReleaseReaderLock();
				}
			}
		}

		public void CopyTo(Array array, int index)
		{
			try
			{
				AccessSem.AcquireReaderLock(-1);
				m_innerTable.CopyTo(array, index);
			}
			finally
			{
				AccessSem.ReleaseReaderLock();
			}
		}

		public object SyncRoot
		{
			get
			{
				//TODO: Is this the right thing todo with a synced hashtable like this one?
				return this;
			}
		}

		#endregion

		#region IEnumerable Members

		IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return new ThreadedHashtableEnumerator(this);
		}

		#endregion

		#region ICloneable Members

		public object Clone()
		{
			return new ThreadSafeHashtable(this);
		}

		#endregion


		protected class ThreadedHashtableEnumerator : IDictionaryEnumerator
		{
			protected ThreadSafeHashtable m_parent;
			protected IDictionaryEnumerator m_innerEnum;

			#region IDictionaryEnumerator Members

			public ThreadedHashtableEnumerator(ThreadSafeHashtable parent)
			{
				m_parent = parent;
				m_innerEnum = parent.m_innerTable.GetEnumerator();
			}

			public object Key
			{
				get
				{
					try
					{
						m_parent.AccessSem.AcquireReaderLock(-1);
						object k = m_innerEnum.Key;
						return k;
					}
					finally
					{
						m_parent.AccessSem.ReleaseReaderLock();
					}

				}
			}

			public object Value
			{
				get
				{
					try
					{
						m_parent.AccessSem.AcquireReaderLock(-1);
						object v = m_innerEnum.Value;
						return v;
					}
					finally
					{
						m_parent.AccessSem.ReleaseReaderLock();
					}
				}
			}

			public DictionaryEntry Entry
			{
				get
				{
					try
					{
						m_parent.AccessSem.AcquireReaderLock(-1);
						DictionaryEntry e = m_innerEnum.Entry;
						return e;
					}
					finally
					{
						m_parent.AccessSem.ReleaseReaderLock();
					}
				}
			}

			#endregion

			#region IEnumerator Members

			public void Reset()
			{
				m_innerEnum.Reset();
			}

			public object Current
			{
				get
				{
					try
					{
						m_parent.AccessSem.AcquireReaderLock(-1);
						object c = m_innerEnum.Current;
						return c;
					}
					finally
					{
						m_parent.AccessSem.ReleaseReaderLock();
					}
				}
			}

			public bool MoveNext()
			{
				return m_innerEnum.MoveNext();
			}

			#endregion

		}


		protected class ThreadedHashtableCollection : ICollection
		{
			protected ThreadSafeHashtable m_parentTable;
			protected ICollection m_myInner;

			public ThreadedHashtableCollection(ThreadSafeHashtable parent, ICollection inner)
			{
				m_parentTable = parent;
				m_myInner = inner;
			}

			#region ICollection Members

			public bool IsSynchronized {get{return true;}}

			public int Count
			{
				get
				{
					try
					{
						m_parentTable.AccessSem.AcquireReaderLock(-1);
						int c = m_myInner.Count;
						return c;
					}
					finally
					{
						m_parentTable.AccessSem.ReleaseReaderLock();
					}
				}
			}

			public void CopyTo(Array array, int index)
			{
				try
				{
					m_parentTable.AccessSem.AcquireReaderLock(-1);
					m_myInner.CopyTo(array, index);
				}
				finally
				{
					m_parentTable.AccessSem.ReleaseReaderLock();
				}
			}

			public object SyncRoot
			{
				get
				{
					return this;
				}
			}

			#endregion

			#region IEnumerable Members

			public IEnumerator GetEnumerator()
			{
				return m_myInner.GetEnumerator();
			}

			#endregion

		}
	}

}
